;================================================================
;  matrix.s
;               The Matrix effect
;
;================================================================
;
; 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
;
; (c)2006 Bodo Wenzel
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License as
; published by the Free Software Foundation; either version 2 of
; the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with this program; if not, write to the Free
; Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
; MA 02111-1307 USA
;================================================================

	.module	matrix

;= Externals ====================================================

	.globl	text_file
	.globl	i_reg
	.globl	heap_ptr
	.globl	seed

	.globl	FRAME_RATE
	.globl	check_break
	.globl	set_show
	.globl	show_dummy
	.globl	wait_frames

	.globl	ZX_FONT_I_REG
	.globl	TEXT_ROWS,TEXT_COLUMNS
	.globl	CHAR_MASK
	.globl	G0,G1,G2,G3,G4,G5,G6,G7,G8,G9,GA,GB,GC,GD,GE,GF
	.globl	__,X8,X9,XA,QU,PD,DL,CL,QM,LP,RP,GT,LT,EQ,PL,MI
	.globl	TI,SL,SC,CM,PE,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9
	.globl	_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P
	.globl	_Q,_R,_S,_T,_U,_V,_W,_X,_Y,_Z,NL,INV
	.globl	show_text
	.globl	t_in_down
	.globl	t_out_down
	.globl	t_ready

	.globl	random

	.globl	unpack

;= Constants ====================================================

; double global definition checked by linker
FRAME_RATE	==	50
TEXT_ROWS	==	24
TEXT_COLUMNS	==	32

RUNS		=	543	; number of runs
COUNT		=	15	; number of concurrent drops
SPEED		=	3	; number of frames per step

ROWS_RANGE	=	16	; must be 2^n
LENGTH_RANGE	=	32	; must be 2^n
LENGTH_OFFSET	=	4
COLUMNS_RANGE	=	32	; must be 2^n
DELAY_RANGE	=	8	; must be 2^n

WAIT		=	5	; in seconds

;= Program code =================================================

	.area	CODE

;- Show the Matrix effect ---------------------------------------

matrix::
	ld	hl,#show_dummy
	call	set_show

	ld	hl,(heap_ptr)
	ld	de,#screen
	ld	bc,#PAC_SCR_SIZE
	call	unpack

	ex	de,hl
	ld	de,#scroll_line
	ld	bc,#PAC_SL_SIZE
	call	unpack

	ld	bc,#RUNS
	ld	e,#0
	call	handle_all	; encode screen

	call	t_in_down

	ld	hl,(heap_ptr)
	ld	(text_file),hl
	ld	a,#ZX_FONT_I_REG
	ld	(i_reg),a
	ld	hl,(heap_ptr)
	ld	de,#TEXT_ROWS*(TEXT_COLUMNS+1)
	add	hl,de
	ld	(show_text),hl
	ld	hl,#show_text
	call	set_show

	ld	bc,#RUNS
	ld	e,#SPEED
	call	handle_all	; decode screen

	ld	b,#WAIT*5
m_wait:
	call	check_break
	jr	nc,m_quit

	ld	a,#FRAME_RATE/5
	call	wait_frames
	djnz	m_wait		; time to read the screen

m_quit:
	call	t_out_down

	jp	t_ready

;- Handle all drops all runs ------------------------------------
; BC number of runs
; E  number of frames to delay between runs

handle_all:
	push	bc
	push	de

	ld	hl,#-1
	ld	(seed),hl	; we need a reproducable start

	ld	b,#COUNT
	ld	hl,#drops
ha_cd_loop:
	push	bc
	call	create_drop
	pop	bc
	djnz	ha_cd_loop

	pop	de
	pop	bc

ha_loop:
	push	bc
	push	de

	ld	a,e
	and	a
	call	nz,wait_frames

	ld	b,#COUNT
	ld	hl,#drops
ha_hd_loop:
	push	bc
	call	handle_drop
	pop	bc
	djnz	ha_hd_loop

	pop	de
	pop	bc

	call	check_break
	ret	nc

	dec	bc
	ld	a,b
	or	c
	jr	nz,ha_loop

	ret

;- Handle a drop ------------------------------------------------
; HL points to its structure:
;	.dw	pointer
;	.db	delay-counter
;	.db	delay-reload
;	.db	length
; On return HL is incremented to the next structure.

handle_drop:
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	inc	hl

	ld	a,(hl)
	dec	a
	ld	(hl),a
	inc	hl
	jr	z,hd_drop	; delay finished?

	ex	de,hl
	ld	c,(hl)
	call	random
	and	#CHAR_MASK
	xor	c
	ld	(hl),a
	ex	de,hl		; modify character

	inc	hl
	inc	hl
	ret

hd_drop:
	ld	a,(de)
	xor	#INV
	ld	(de),a		; release character

	ld	a,(hl)
	dec	hl
	ld	(hl),a		; restore delay

	ex	de,hl
	ld	bc,#TEXT_COLUMNS+1
	add	hl,bc
	ex	de,hl
	dec	hl
	ld	(hl),d
	dec	hl
	ld	(hl),e		; update pointer

	ld	c,l
	ld	b,h
	inc	bc
	inc	bc
	inc	bc
	inc	bc
	ld	a,(bc)
	dec	a
	ld	(bc),a
	jr	z,create_drop	; drop finished?

	inc	bc
	ld	l,c
	ld	h,b
	jr	cd_reserve

;- Creates a new drop -------------------------------------------
; HL points to its structure:
;	.dw	pointer
;	.db	delay-counter
;	.db	delay-reload
;	.db	length
; On return HL is incremented to the next structure.

create_drop:
	call	random
	rrca
	ld	b,a
	sbc	a,a
	and	b
	and	#ROWS_RANGE-1
	ld	b,a		; first row

	call	random
	and	#LENGTH_RANGE-1
	add	#LENGTH_OFFSET
	ld	c,a		; length

	add	a,b
	sub	#TEXT_ROWS+1
	jr	c,cd_len_ok
	sub	c
	cpl
	ld	c,a		; limit length
cd_len_ok:

	push	hl
	ld	hl,(heap_ptr)
	ld	de,#TEXT_COLUMNS+1
	inc	b
cd_ptr_loop:
	add	hl,de
	djnz	cd_ptr_loop
	sbc	hl,de		; first row -> pointer

	call	random
	and	#COLUMNS_RANGE-1
	ld	e,a		; D is already 0
	add	hl,de		; add column to pointer

	ex	de,hl
	pop	hl
	ld	(hl),e
	inc	hl
	ld	(hl),d
	inc	hl		; pointer

	call	random
	and	#DELAY_RANGE-1
	inc	a
	ld	(hl),a
	inc	hl
	ld	(hl),a
	inc	hl		; delay

	ld	(hl),c
	inc	hl		; length

cd_reserve:
	ld	a,(de)
	xor	#INV
	ld	(de),a		; reserve character
	ret

;= Data =========================================================

screen:
	.include	"matr_scr.inc"
PAC_SCR_SIZE	=	.-screen
UNP_SCR_SIZE	=	UNPACKED
	.if	UNP_SCR_SIZE-TEXT_ROWS*(TEXT_COLUMNS+1)
	.error	"Wrong size of screen text"
	.endif

scroll_line:
	.include	"matr_sl.inc"
PAC_SL_SIZE	=	.-scroll_line
UNP_SL_SIZE	=	UNPACKED

;= Variables ====================================================

	.area	SCRATCH	(ovr)

; Each drop uses this structure:
;	.dw	pointer
;	.db	delay-counter
;	.db	delay-reload
;	.db	length
drops:
	.ds	COUNT*5

;= Heap usage ===================================================

;		unpacked screen
HEAP =		TEXT_ROWS*TEXT_COLUMNS
;		unpacked scroll text
HEAP =	HEAP +	UNP_SL_SIZE

	.area	HEAP	(abs,ovr)
	.ds	HEAP

;= The end ======================================================
